﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using System.Collections.ObjectModel;
using System.Collections.Specialized;
using Microsoft.Xna.Framework.Input;
using System.Threading;
using UCS.XNA.Utils.Animation;

namespace UCS.XNA.Utils
{
    public class Menu : IDrawable, IFadeAnimatable
    {
        #region Constants

        private const float MENU_BLANK_PERCENTAGE = 0.05f;

        #endregion

        #region Attributes and Properties

        private Rectangle _bounds;
        public Rectangle Bounds
        {
            get { return this._bounds; }
            set
            {
                this._bounds = value;

                this.RearrangeMenuItemsBounds();
            }
        }

        private bool _visible;
        public bool Visible
        {
            get { return this._visible; }
            set { this._visible = value; }
        }

        private bool _enabled;
        public bool Enabled
        {
            get { return this._enabled; }
            set { this._enabled = value; }
        }

        private float _transparency;
        public float Transparency
        {
            get { return this._transparency; }
            set { this._transparency = value; }
        }

        public int DrawOrder
        {
            get { throw new NotImplementedException(); }
        }

        private int _spacing = 5;
        public int Spacing
        {
            get { return _spacing; }
            set
            {
                _spacing = value;
                this.RearrangeMenuItemsBounds();
            }
        }

        #endregion

        #region Private Members

        private RefreshableCollection<MenuItem> _menuItems;
        private bool _isDrawing;
        private MenuItem _focusedItem;

        #endregion

        #region Constructors

        public Menu(Rectangle bounds)
        {
            this._bounds = bounds;
            this._menuItems = new RefreshableCollection<MenuItem>();
            this._menuItems.CollectionChanged += new NotifyCollectionChangedEventHandler(this.MenuItemsCollectionChanged);
            this._visible = true;
            this._enabled = true;
        }

        #endregion

        #region Events

        public event EventHandler<EventArgs> EnabledChanged;
        public event EventHandler<EventArgs> UpdateOrderChanged;
        public event EventHandler<EventArgs> DrawOrderChanged;
        public event EventHandler<EventArgs> VisibleChanged;
        public event EventHandler<EventArgs> Hid;
        public event EventHandler<EventArgs> Showed;

        #endregion

        #region Public Methods

        public void AddItem(MenuItem menuItem)
        {
            menuItem.Hid += (sender, e) =>
                {
                    if (!this._menuItems.Any(item => item.Visible))
                    {
                        this._visible = false;
                        this.RaiseHidEvent();
                    }
                };
            menuItem.Showed += (sender, e) =>
            {
                if (!this._menuItems.Any(item => !item.Visible))
                    this.RaiseShowedEvent();
            };
            menuItem.GotFocus += this.MenuItemGotFocus;

            this._menuItems.Add(menuItem);
        }

        public void RemoveItem(MenuItem menuItem)
        {
            this._menuItems.Remove(menuItem);
        }

        public void RemoveItem(int index)
        {
            this._menuItems.Remove(this._menuItems[index]);
        }

        public void Update(GameTime gameTime, MouseState mouseState, KeyboardState keyboardState)
        {
            if (!this._isDrawing)
                this._menuItems.Refresh();

            foreach (var menuItem in this._menuItems)
                menuItem.Update(gameTime, mouseState, keyboardState);
        }

        public void Draw(GameTime gameTime)
        {
            if (this._visible)
            {
                this._isDrawing = true;

                foreach (var menuItem in this._menuItems)
                    menuItem.Draw(gameTime);

                this._isDrawing = false;
            }
        }

        public void Show()
        {
            this._visible = true;
            this._enabled = true;

            foreach (var menuItem in this._menuItems)
                menuItem.Show();
        }

        public void Hide()
        {
            this._enabled = false;

            if (this._menuItems.Count == 0)
            {
                this._visible = false;
                this.RaiseHidEvent();
            }
            else
            {
                foreach (var menuItem in this._menuItems)
                    menuItem.Hide();
            }
        }

        #endregion

        #region Private Methods

        private void RearrangeMenuItemsBounds()
        {
            if (!_menuItems.Any())
                return;

            int itemWidth = _bounds.Width;
            int itemHeight = (_bounds.Height - (_menuItems.Count - 1) * _spacing) / _menuItems.Count;

            for (int i = 0; i < _menuItems.Count; i++)
            {
                Rectangle rect = new Rectangle();
                rect.X = _bounds.X;
                rect.Y = i * itemHeight + _bounds.Y + i * _spacing;
                rect.Width = itemWidth;
                rect.Height = itemHeight;

                _menuItems[i].Bounds = rect;
            }
        }

        private void RaiseHidEvent()
        {
            if (this.Hid != null)
                this.Hid(this, new EventArgs());
        }

        private void RaiseShowedEvent()
        {
            if (this.Showed != null)
                this.Showed(this, new EventArgs());
        }

        #endregion

        #region Signed Events Methods

        private void MenuItemsCollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
        {
            this.RearrangeMenuItemsBounds();

            foreach (MenuItem menuItem in e.NewItems)
            {
                if (this._visible)
                    menuItem.Show();
                else
                    menuItem.Hide();
            }
        }

        private void MenuItemGotFocus(object sender, EventArgs e)
        {
            this._focusedItem = sender as MenuItem;

            foreach (var item in this._menuItems)
            {
                if (item != this._focusedItem)
                    item.Focused = false;
            }
        }

        #endregion
    }
}
